Jx.categorize('unit.tuple', {

	$extend: 'Jx.lib.Unit',

	$requires: [ 'Jx.lib.Array' ],

	$cascadeCallback: function(Class, Base){

		var $A = Jx.lib.Array,

			accessors = Class.$data.names,

			Cproto = Class.prototype,

			Bproto = Base.prototype,

			types = Cproto.types,

			baseTypes = Bproto.types,

			definition = Class.definition,

			overrides = {};

		//-- inherit order
		Cproto.order = $A.unique(Cproto.order, accessors);

		if (!Jx.isObject(types)){

			Cproto.types = types = {};

		}

		//-- create types from accessors
		for(var name, setter, fn, c = 0, l = accessors.length; c<l; c++){

			if (!types.hasOwnProperty(name = accessors[c])){

				types[name] = baseTypes && baseTypes.hasOwnProperty(name) ?

					baseTypes[name]

					:

					//-- use default type defined
					Cproto.defaultType;

			}

			//-- fix setter
			setter = 'onSet'+name.charAt(0).toUpperCase()+name.substring(1, name.length);

			if (!definition.hasOwnProperty(setter) && !(setter in Bproto)){

				fn = overrides[setter] = function(){

					this.set.apply(this, [arguments.callee.setterFor].concat(

						Array.prototype.slice.call(arguments, 1)

					));

				}

				fn.setterFor = name;

			}

		}

		Class.override(overrides);

	},

	defaultType: 'scalar',

	defaultToStringSeparator: ',',

	defaultToObjectMethod: 'toString',

	defaultEachItemValue: 'toString',

	order: [],

	types: {},

	indexRe: /^([0-9]+)$/,

	constructor: function(){

		//-- initialize
		var order = this.order,

			types = this.types,

			data = this.$cache;

		for(var name, l = order.length; l--;){

			name = order[l];

			data[ name ] = Jx.instantiate( 'unit.' + types[name], [data[name]] );

		}

		this.$super(arguments);

	},

	onSetValue: function(operation){

		var values = operation.values,

			setters = operation.setters;

		for(var l = setters.length; l--;){

			this.set( setters[l], values[l] );

		}

	},

	//-- set value
	allowSetValue: function(operation, setter, value){

		var setters = [],

			values = [],

			order = this.order;

		operation.me = this;

		operation.setters = setters;

		operation.values = values;

		if (Jx.isObject(setter)){

			Jx.each(setter, function(n, v, o){

				var setters = this.setters,

					values = this.values,

					name;

				if (o.hasOwnProperty(n) &&

				   (name = this.me.getIndexedName(n))

				){

					setters[ setters.length ] = name;

					values[ values.length ] = values;

				}

			}, operation);

		} else if (Jx.isArray(setters)){

			for(var order = this.order, l = Math.min(order.length, setters.length); l--;){

				setters[ l ] = this.getIndexedName(l);

				values[ l ] = setters[l];

			}

		//-- direct
		} else if (

			(setter = this.getIndexedName(setter))

		){

			setters[0] = setter;

			values[0] = value;

		}

		return operation.setters.length > 0;

	},

	get: function(index){

		return this.$super(

			[ this.getIndexedName(index) ].concat(

				Array.prototype.slice.call(arguments, 1)

			)

		);

	},

	getIndexedName: function(index){

		var order = this.order,

			name = void(0);

		if (Jx.isString(index, true)){

			if (this.indexRe.test(index)){

				index = parseInt(index, 10);

			} else if (this.types.hasOwnProperty(index)){

				name = index;

			}

		}

		if (Jx.isNumber(index) &&

			index in order

		){

			name = order[index];

		}

		return name;

	},

	set: function(index){

		var item = this.get( index );

		if (item){

			item.setValue.apply(this, Array.prototype.slice.call(arguments, 1));

		}

		return this;

	},

	toString: function(separator, argumentIndex){

		return this.toArray('toString', argumentIndex).join(

			Jx.isString(separator) ?

				separator

				:

				this.defaultToStringSeparator

		);

	},

	toArray: function(method, argumentIndex){

		var results = [];

		this.eachItem(function(me, name, value, index){
			
			if (value){
				
				this[index] = value;
				
			} else {
				
				this.splice(index, 1);
				
			}

		}, results, method, argumentIndex);

		return results;

	},

	toObject: function(method, argumentIndex){

		var results = {};

		if (Jx.isObject(method)){

			argumentIndex = method;

		}

		if (!Jx.isString(method, true)){

			method = this.defaultToObjectMethod;

		}

		this.eachItem(function(me, name, value, index){

			this[index] = this[name] = value;

		}, results, method, argumentIndex);

		return results;

	},

	eachItem: function(fn, scope, itemMethod, argumentIndex){

		var result = void(0),

			defaultMethod = this.defaultEachItemValue,

			defaultArguments = [];

		if (Jx.isFunction(fn)){

			if (!Jx.isObject(argumentIndex)){

				argumentIndex = {};

			}

			if (!Jx.isString(itemMethod, true)){

				itemMethod = this.defaultEachItemValue;

			}

			var names = this.constructor.$data.names;

			for(var name, value, args, item, o, l = names.length; l--;){

				name = names[l];

				args = argumentIndex.hasOwnProperty(name) ?

					argumentIndex[name]

					:

					[];
				
				item = this[

					'get' + name.charAt(0).toUpperCase() + name.substring(1, name.length)

				]();
				
				value = item[ itemMethod ].apply(item, (Jx.isArray(args) && args) || []);

				if (fn.call(scope, this, name, value, l, o = this.get(name)) === false){
				
					result = o;
				
					break;
				
				}

			}

		}

		return result;

	}



});
